﻿namespace ExcelTool{
    using System;
    using System.Collections.Generic;
    using libxl;
    using System.Text;
    using System.IO;

    public partial class Converter{
        public struct ReferCheckResult {
            public class ExcelCountRec {
                public string name;
                public int valid_data_count;
            }
            public List<string> valueList { get; private set; }
            public List<int> rowlist { get; private set; }
            public List<ExcelCountRec> excelCountRecord { get; private set; }//级联表格名称->有效数据个数, 用于查找某个数据位于哪个excel表格
            public Dictionary<string, int> excelColumnCache{ get; private set; } //第几列

            public void AddValue(string value, int row_index) {
                if(valueList == null) {
                    valueList = new List<string>();
                }
                if(rowlist == null) {
                    rowlist = new List<int>();
                }
                valueList.Add(value);
                rowlist.Add(row_index);
            }
            public void AddCount(string excelName, int count) {
                if(excelCountRecord == null) {
                    excelCountRecord = new List<ExcelCountRec>();
                }
                ExcelCountRec rec = new ExcelCountRec();
                rec.name = excelName;
                rec.valid_data_count = count;
                excelCountRecord.Add(rec);
            }
            public void AddColumnCache(string excelName, int column) {
                if(excelColumnCache == null) {
                    excelColumnCache = new Dictionary<string, int>();
                }
                excelColumnCache.Add(excelName, column);
            }
            public string GetExcelNameByIndex(int i) {
                int countsum = 0;
                foreach(var item in excelCountRecord) {
                    countsum += item.valid_data_count; 
                    if(countsum > i) {
                        return item.name;
                    }
                }
                return string.Empty;
            }

            public string GetAllExcelName() {
                StringBuilder sb = new StringBuilder();
                foreach(var item in excelColumnCache) {
                    sb.AppendFormat("Excel:{0} \n", item.Key);
                }

                return sb.ToString();
            }
        }
        bool GetColumnData(XmlMetalib lib, string className, string sheet_name, string column_name, out ReferCheckResult result) {
            result = new ReferCheckResult();
            XmlClassSet cs = lib.GetClassSet(className);
            if(cs == null) {
                return false;    
            }
            foreach(XmlDataSource ds in cs.DataSourceList) {
                Book xlBook = m_excel[ds.tablename];
                Sheet sheet;
                if(string.IsNullOrEmpty(sheet_name)) {
                    sheet= xlBook.getSheet(EXCEL_FILE_RULE.MAIN_DATA_SHEET_INDEX);  //不填sheetname默认引用第一张Sheet
                }
                else {
                    if(!GetSheetByName(ds.tablename, sheet_name, out sheet)) return false;
                }
                int column = -1;
                if(!_getColIndex(sheet, column_name, out column)) return false;
                result.AddColumnCache(ds.tablename, column);

                int count = 0;
                for(count = EXCEL_FILE_RULE.TABLEHEAD_LINE_COUNT; count < sheet.lastRow(); count++) {
                    string val = sheet.readStr(count, column);
                    result.AddValue(val, count);
                }
                result.AddCount(ds.tablename, count - EXCEL_FILE_RULE.TABLEHEAD_LINE_COUNT);
            }

            return true;
        }

        #region 环引用检查
        class INDEGREE_REC {
            public int inner_ref; //共用库的内部引用
            public int outter_ref; //外部引用
            public string name;

            public int ref_count { get { return inner_ref + outter_ref; } }
            public int iterator_count;
        }
        void createRecord(string refer_type_name, bool b_common) {
            Dictionary<string, INDEGREE_REC> lib;
            if(b_common) {
                lib = common_indegree;
            }
            else {
                lib = lib_indegree;
            }
            if(lib.ContainsKey(refer_type_name)) {
                return;
            }
            INDEGREE_REC record = new INDEGREE_REC(); ;
            record.inner_ref = 0;
            record.outter_ref = 0;
            record.iterator_count = 0;
            record.name = refer_type_name;
            lib[refer_type_name] = record;
        }
        void recordIndgree(XmlMetalib metalib, string refer_type_name, bool common) {
            Dictionary<string, INDEGREE_REC> lib;
            if(common) {
                lib = common_indegree;
            }
            else {
                lib = lib_indegree;
            }
            if(metalib.ReferenceSets.ContainsKey(refer_type_name)) {
                if(lib.ContainsKey(refer_type_name)) {
                    var record = lib[refer_type_name];
                    record.inner_ref++;
                }
                else {
                    createRecord(refer_type_name, common);
                }
            }
            else if(common_indegree.ContainsKey(refer_type_name)){
                //没有再找公共lib
                INDEGREE_REC common_rec = common_indegree[refer_type_name];
                common_rec.outter_ref++;
            }
        }
        void recordReferSetIndegree(XmlMetalib lib) {
            lib_indegree.Clear();
            foreach(var item in lib.ReferenceSets) {
                XmlBaseRefer refer_item = item.Value as XmlBaseRefer;
                if(refer_item != null) {
                    createRecord(refer_item.name, lib.common);       //创建所有被引用结构的引用计数对象
                    foreach(var f_it in refer_item.fieldCfg) {
                        if(XML_DATA_TYPE_NAME.IsReferenceType(f_it.type)) {
                            recordIndgree(lib, f_it.type_param, lib.common);
                        }
                    }
                }
            }
        }
        Dictionary<string, INDEGREE_REC> common_indegree = new Dictionary<string, INDEGREE_REC>(); //入度记录
        Dictionary<string, INDEGREE_REC> lib_indegree = new Dictionary<string, INDEGREE_REC>(); //库的入度记录
        Stack<string> zero_indegree_stack = new Stack<string>(); //零入度Stack
        void resetCommonOutterRef() {
            foreach(var item in common_indegree) {
                INDEGREE_REC common = item.Value;
                common.outter_ref = 0;
            }
        }
        void pushAllZeroDegreeNode() {
            zero_indegree_stack.Clear();
            Action<INDEGREE_REC> pushAction = (INDEGREE_REC degree_rec) => {
                degree_rec.iterator_count = degree_rec.ref_count;
                if(degree_rec.ref_count == 0) {
                    zero_indegree_stack.Push(degree_rec.name);
                }
            };
            foreach(var item in lib_indegree) {
                pushAction(item.Value);
            }
            foreach(var item in common_indegree) {
                pushAction(item.Value);
            }
        }
        INDEGREE_REC getIndegreeRecord(string target_name) {
            if(lib_indegree.ContainsKey(target_name)){
                return lib_indegree[target_name];
            }
            if(common_indegree.ContainsKey(target_name)) {
                return common_indegree[target_name];
            }
            return default(INDEGREE_REC);
        }

        XmlBaseSet getReferSet(XmlMetalib module_lib, string name) {
            if(module_lib.ReferenceSets.ContainsKey(name)) {
                return module_lib.ReferenceSets[name];
            }
            foreach(var common_lib in m_allCommonLibs) {
                if(common_lib.ReferenceSets.ContainsKey(name)) {
                    return common_lib.ReferenceSets[name];
                }
            }
            return null;
        }
        public bool CycleValid() {
            common_indegree.Clear();
            //先记录所有公共lib的内部referSet的入度，即被引用的次数
            foreach(var common_lib in m_allCommonLibs) {
                recordReferSetIndegree(common_lib);
            }
            List<string> valid_result_list = new List<string>();
            //每一个库和公共库拼合引用数据，做环引用检查
            foreach(var item in m_allLib) {
                resetCommonOutterRef(); //重置公共库的外部引用
                recordReferSetIndegree(item.Value);  //记录每个库的引用数据
                pushAllZeroDegreeNode();
                int count = 0;
                while(zero_indegree_stack.Count > 0) {
                    string start_node = zero_indegree_stack.Pop();
                    if(!valid_result_list.Contains(start_node)) {
                        valid_result_list.Add(start_node);
                    }
                    ++count;

                    XmlBaseSet baseSet = getReferSet(item.Value, start_node);
                    if(baseSet == null) continue;
                    XmlBaseRefer baseRefer = baseSet as XmlBaseRefer;    
                    if(baseRefer == null) continue;

                    foreach(XmlFieldEntry fEntry in baseRefer.fieldCfg) {
                        if(XML_DATA_TYPE_NAME.IsReferenceType(fEntry.type)) {
                            INDEGREE_REC rec = getIndegreeRecord(fEntry.type_param);
                            if(--rec.iterator_count == 0) zero_indegree_stack.Push(fEntry.type_param);
                        }
                    }
                }
                if(count < common_indegree.Count + lib_indegree.Count) {
                    foreach(var it in common_indegree) {
                        if(!valid_result_list.Contains(it.Key)) {
                            Reporter.PushError("检测到环引用{0}", it.Key);
                        }
                    }
                    foreach(var it in lib_indegree) {
                        if(!valid_result_list.Contains(it.Key)) {
                            Reporter.PushError("检测到环引用{0}", it.Key);
                        }
                    }
                    return false;
                }
            }
            return true; 
        }
        #endregion

        //根据配置的Valid，做数据检查
        public bool ExcelDataValid() {
            foreach(var lib_it in m_allLib) { //遍历每个lib
                XmlMetalib lib = lib_it.Value;
                if(lib.valid_config == null || lib.valid_config.validEntryList == null || lib.valid_config.validEntryList.Count <= 0) {
                    continue;
                }
                foreach(XmlValidEntry ve in lib.valid_config.validEntryList) {
                    ReferCheckResult check_list; 
                    if(!GetColumnData(lib, ve.class_name, ve.sheet_name, ve.column_name, out check_list)) return false;

                    ReferCheckResult ref_list;
                    if(!GetColumnData(m_allLib[ve.ref_metalibName], ve.ref_className, string.Empty, ve.ref_columnName, out ref_list)) return false;
                    for(int i = 0; i < check_list.valueList.Count; i++) {
                        string check_val = check_list.valueList[i];
                        if(string.IsNullOrEmpty(check_val)) {
                            continue;
                        }
                        if(!ref_list.valueList.Contains(check_val)) {
                            string excelName = check_list.GetExcelNameByIndex(i);
                            Reporter.PushError("单元值【{1}】\n位于{0}\n无法在\n{2}的列【{3}】中找到", 
                                                    Reporter.MakeExcelCellInfo(excelName, ve.sheet_name, check_list.rowlist[i], check_list.excelColumnCache[excelName]), 
                                                    check_val,
                                                    ref_list.GetAllExcelName(),
                                                    ve.ref_columnName);
                            return false;
                        }
                    }
                }
            }

            return true;
        }

        public bool BlankValid() {
            bool totalBlankFlag = false;
            foreach(var e_it in m_excel) {
                Book book = e_it.Value;
                for(int i = 0; i < book.sheetCount(); i++) {
                    Sheet sheet = book.getSheet(i);
                    for(int row_it = 0; row_it < sheet.lastRow(); row_it++) {
                        bool blankflag = true;
                        for(int col_it = 0; col_it < sheet.lastCol(); col_it++) {
                            CellType ct = sheet.cellType(row_it, col_it);
                            if(!ifCellBlank(ct)) {
                                blankflag = false;
                                continue;
                            }
                        }
                        if(blankflag) {
                            totalBlankFlag = true;
                            Reporter.PushError("检测到空白行 Excel: {0}, Sheet: {1}, Row: {2}, 请删除", book.FilePath, sheet.name, row_it + 1);
                        }
                    }
                }
            }

            if(totalBlankFlag) return false;

            return true;
        }

        public bool PreCheck() {
            if(!ExcelDataValid()) return false;
            if(!BlankValid()) return false;
            if(!CycleValid()) return false;
            return true;
        }


    }
}
